package genetic.q4;

import java.util.LinkedList;
import java.util.List;

import genetic.Function;
import genetic.Genotype;
import genetic.Phenotype;
import genetic.Terminal;
import genetic.TreeNode;
import genetic.q4.functions.DoNothing;
import genetic.q4.functions.IfXClearPutElse;
import genetic.q4.functions.PutRandom;
import genetic.q4.functions.SwitchX;

public class TicTacToePhenotype extends Phenotype {

	private static final int INIT_DMAX = 4;
	private static final int MUTATE_DMAX = 3;

	double average = 0;

	public TicTacToePhenotype() {
		super();
		List<Function> functions = new LinkedList<Function>();
		List<Terminal> terminals = new LinkedList<Terminal>();
//		for (int i=0; i<18; i++){
		functions.add(new IfXClearPutElse());

//		}
//		for (int i=0; i<6; i++){
		functions.add(new SwitchX());
//		}

//		functions.add(new TryThreeSpecial());
		//		functions.add(new TryThree());
		terminals.add(new DoNothing());
		//		terminals.add(new PutRandomForOther());
		//		terminals.add(new PutRandom());
		//		terminals.add(new PutMin());
		//		terminals.add(new PutMinForOther());
		init(functions, terminals);
	}

	@Override
	public Genotype[] crossover(Genotype g1, Genotype g2) {
		return crossover2(g1, g2);
	}

	@Override
	public double fitness(Genotype g) {
		return ((TicTacToeGenotype)g).getScore()*Math.abs(average)/100.0/100.0;
	}

	@Override
	public List<Genotype> getInitialPopulation(int populationSize) {
		return getInitialPopulationCommon(populationSize, INIT_DMAX);
		//		List<Genotype> pop = new LinkedList<Genotype>();
		//		Vector<Integer> numbers = new Vector<Integer>();
		//		for (int i=0; i<9; i++){
		//			numbers.add(i);
		//		}
		//		for (int i=0; i<populationSize; i++){
		//			Collections.shuffle(numbers);
		//			TreeNode t = new DoNothing();
		//			for (int j = 0; j < 9; j++) {
		//				t = new IfXClearPutElse(numbers.get(j), t);
		//			}
		//			pop.add(new TicTacToeGenotype(t));
		//		}
		//		return pop;
	}

	@Override
	public Genotype makeInstanceFromTree(TreeNode tree) {
		return new TicTacToeGenotype(tree);
	}

	@Override
	public Genotype mutate(Genotype g) {
		if (Math.random()<0.25){
			return super.mutateDefault(((TicTacToeGenotype)g).minimize(), MUTATE_DMAX);
		} else {
			return super.mutateDefault(g, MUTATE_DMAX);
		}
	}

	@Override
	public Genotype[] selectParents(List<Genotype> population, int populationSize) {
		return super.selectMatingPoolTournamentSelection(population, populationSize, 6, 2, 0.4);
//		return super.selectMatingParentsRouletteWheel(population, populationSize);
	}

	@Override
	public void updateFitness(List<Genotype> population) {
		average = 0;
		for (int i = 0; i < population.size(); i++) {
			((TicTacToeGenotype)population.get(i)).resetScore();
		}
		TicTacToeGenotype randomPlayer = new TicTacToeGenotype(new PutRandom());
		for (int i = 0; i < population.size(); i++) {
			for (int j=0; j<100; j++){
				TicTacToeGenotype g1 = (TicTacToeGenotype)population.get(i);
				TicTacToeBoard b = new TicTacToeBoard();
				int winner = b.playGame(g1, randomPlayer);

				if (winner == TicTacToeBoard.WINNER_X){
					g1.addScore(7);
				} else if (winner == TicTacToeBoard.WINNER_O){
				} else if (winner == TicTacToeBoard.WINNER_DRAW){
					g1.addScore(4);
				} else {
					g1.addScore(2);
				}

				b.clear();
				winner = b.playGame(randomPlayer, g1);
				if (winner == TicTacToeBoard.WINNER_O){
					g1.addScore(9);
				} else if (winner == TicTacToeBoard.WINNER_X){
				} else if (winner == TicTacToeBoard.WINNER_DRAW){
					g1.addScore(4);
				} else {
					g1.addScore(2);
				}

			}
			for (int j = i+1; j < population.size(); j++) {
				TicTacToeBoard b = new TicTacToeBoard();
				TicTacToeGenotype g1 = (TicTacToeGenotype)population.get(i);
				TicTacToeGenotype g2 = (TicTacToeGenotype)population.get(j);
				int winner = b.playGame(g1, g2);

				if (winner == TicTacToeBoard.WINNER_X){
					g1.addScore(10);
				} else if (winner == TicTacToeBoard.WINNER_O){
					g2.addScore(12);
				} else if (winner == TicTacToeBoard.WINNER_DRAW){
					g1.addScore(5);
					g2.addScore(5);
				} else {
					g1.addScore(3);
					g2.addScore(3);
					//					g1.addScore(-1);
					//					g2.addScore(-1);
				}

				b.clear();
				winner = b.playGame(g2, g1);

				if (winner == TicTacToeBoard.WINNER_X){
					g2.addScore(10);
				} else if (winner == TicTacToeBoard.WINNER_O){
					g1.addScore(12);
				} else if (winner == TicTacToeBoard.WINNER_DRAW){
					g1.addScore(5);
					g2.addScore(5);
				} else {
					g1.addScore(3);
					g2.addScore(3);
					//					g1.addScore(-1);
					//					g2.addScore(-1);
				}
			}
		}
		for (int i = 0; i < population.size(); i++) {
			average+=((TicTacToeGenotype)population.get(i)).getScore();
		}
	}

	public static void main(String[] args) {
		TicTacToePhenotype p = new TicTacToePhenotype();
		List<Genotype> l = p.getInitialPopulation(1);
		((TicTacToeGenotype)(l.get(0))).getTree().updateTotalKids();
		System.out.println(((TicTacToeGenotype)(l.get(0))).getTree().getTotalNumKids());
		System.out.println("Init");
		System.out.println(l.get(0));
		System.out.println("Mutation");
		System.out.println(p.mutate(l.get(0)));
		System.out.println(p.mutate(l.get(0)));
		System.out.println(p.mutate(l.get(0)));
		System.out.println(p.mutate(l.get(0)));
		System.out.println(p.mutate(l.get(0)));
		System.out.println("Crossover");
		System.out.println(p.crossover(l.get(0), l.get(0))[0]);
		System.out.println(p.crossover(l.get(0), l.get(0))[0]);
		System.out.println(p.crossover(l.get(0), l.get(0))[0]);
		System.out.println(p.crossover(l.get(0), l.get(0))[0]);
		System.out.println(p.crossover(l.get(0), l.get(0))[0]);
	}

	@Override
	public void doEvery50(List<Genotype> population) {
		for (Genotype g: population){
			if (Math.random() < 0.5){
				TicTacToeGenotype tttg = ((TicTacToeGenotype)g);
				tttg.minimizeSelf();
			}
		}
	}

}
